package com.nuanshui.heatedloan.util;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Assert;

/**
 * 反射工具类
 * 
 * @author 陈泽权
 * 
 *         下午2:03:54
 */
public class ReflectionUtils {

	public static Object invokeGetterMethod(final Object obj,
			final String propertyName) {
		final String getterMethodName = (new StringBuilder()).append("get")
				.append(StringUtils.capitalize(propertyName)).toString();
		return invokeMethod(obj, getterMethodName, new Class[0], new Object[0]);
	}

	public static void invokeSetterMethod(final Object obj,
			final String propertyName, final Object value) {
		invokeSetterMethod(obj, propertyName, value, null);
	}

	public static void invokeSetterMethod(final Object obj,
			final String propertyName, final Object value,
			final Class propertyType) {
		final Class type = propertyType == null ? value.getClass()
				: propertyType;
		final String setterMethodName = (new StringBuilder()).append("set")
				.append(StringUtils.capitalize(propertyName)).toString();
		invokeMethod(obj, setterMethodName, new Class[] { type },
				new Object[] { value });
	}

	public static Object getFieldValue(final Object obj, final String fieldName) {
		final Field field = getAccessibleField(obj, fieldName);
		if (field == null)
			throw new IllegalArgumentException((new StringBuilder())
					.append("Could not find field [").append(fieldName)
					.append("] on target [").append(obj).append("]").toString());
		Object result = null;
		try {
			result = field.get(obj);
		} catch (final IllegalAccessException e) {
			e.printStackTrace();
		}
		return result;
	}

	public static void setFieldValue(final Object obj, final String fieldName,
			final Object value) {
		final Field field = getAccessibleField(obj, fieldName);
		if (field == null)
			throw new IllegalArgumentException((new StringBuilder())
					.append("Could not find field [").append(fieldName)
					.append("] on target [").append(obj).append("]").toString());
		try {
			field.set(obj, value);
		} catch (final IllegalAccessException e) {
			e.printStackTrace();
		}
	}

	public static Field getAccessibleField(final Object obj,
			final String fieldName) {
		Assert.notNull(obj, "object\u4E0D\u80FD\u4E3A\u7A7A");
		Assert.hasText(fieldName, "fieldName");
		for (Class superClass = obj.getClass(); !superClass.getSimpleName()
				.equals("Object");)
			try {
				final Field field = superClass.getDeclaredField(fieldName);
				field.setAccessible(true);
				return field;
			} catch (final NoSuchFieldException e) {
				superClass = superClass.getSuperclass();
			}

		return null;
	}

	public static Object invokeMethod(final Object obj,
			final String methodName, final Class parameterTypes[],
			final Object args[]) {
		final Method method = getAccessibleMethod(obj, methodName,
				parameterTypes);
		if (method == null)
			throw new IllegalArgumentException((new StringBuilder())
					.append("Could not find method [").append(methodName)
					.append("] on target [").append(obj).append("]").toString());
		try {
			return method.invoke(obj, args);
		} catch (final Exception e) {
			if (e.getClass().getSimpleName()
					.equals("javax.validation.ConstraintViolationException"))
				e.printStackTrace();
			throw convertReflectionExceptionToUnchecked(e);
		}
	}

	public static Method getAccessibleMethod(final Object obj,
			final String methodName, final Class parameterTypes[]) {
		Assert.notNull(obj, "对象不能为空");
		for (Class superClass = obj.getClass(); !superClass.getSimpleName()
				.equals("Object");)
			try {
				final Method method = superClass.getDeclaredMethod(methodName,
						parameterTypes);
				method.setAccessible(true);
				return method;
			} catch (final NoSuchMethodException e) {
				superClass = superClass.getSuperclass();
			}

		return null;
	}

	public static Class getSuperClassGenricType(final Class clazz) {
		return getSuperClassGenricType(clazz, 0);
	}

	public static Class getSuperClassGenricType(final Class clazz,
			final int index) {
		final Type genType = clazz.getGenericSuperclass();
		try {
			if (!(genType instanceof ParameterizedType)) {
				return Class.forName("java.lang.Object");
			}
			final Type params[] = ((ParameterizedType) genType)
					.getActualTypeArguments();
			if (index >= params.length || index < 0) {
				return Class.forName("java.lang.Object");
			}
			if (!(params[index] instanceof Class)) {
				return Class.forName("java.lang.Object");
			} else {
				return (Class) params[index];
			}
		} catch (final ClassNotFoundException e) {
			e.printStackTrace();
		}
		return null;
	}

	public static RuntimeException convertReflectionExceptionToUnchecked(
			final Exception e) {
		if ((e instanceof IllegalAccessException)
				|| (e instanceof IllegalArgumentException)
				|| (e instanceof NoSuchMethodException))
			return new IllegalArgumentException("Reflection Exception.", e);
		if (e instanceof InvocationTargetException)
			return new RuntimeException("Reflection Exception.",
					((InvocationTargetException) e).getTargetException());
		if (e instanceof RuntimeException)
			return (RuntimeException) e;
		else
			return new RuntimeException("Unexpected Checked Exception.", e);
	}

}